package com.easy.carpanel.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import com.easy.carpanel.R;

import androidx.annotation.Nullable;

public class ClockView extends View {
    private static final float DEFAULT_ANGLE_MIN = 180.0f;
    private static final float DEFAULT_ANGLE_MAX = 360.0f;
    private static final int DEFAULT_RADIUS_DIAL = 128;
    private static final int DEFAULT_STROKE_WIDTH = 6;

    private float min;
    private float max;
    private Bitmap background;
    private int radiusDial;

    float oldPercent = 0f;
    float percent;
    float pointRate = 1f;
    private Rect mRect;

    private Paint pointerPaint;
    private Path pointerPath;
    private ValueAnimator valueAnimator;
    private long animatorDuration;
    TimeInterpolator interpolator = new SpringInterpolator();

    public ClockView(Context context) {
        this(context, null);
    }

    public ClockView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initPaint();
    }

    private void initAttrs(Context context, AttributeSet attrs){
        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ClockView);
        min = attributes.getFloat(R.styleable.ClockView_angleMin, DEFAULT_ANGLE_MIN);
        max = attributes.getFloat(R.styleable.ClockView_angleMax, DEFAULT_ANGLE_MAX);
        radiusDial = (int) attributes.getDimension(R.styleable.ClockView_radiusDial, dp2px(DEFAULT_RADIUS_DIAL));
        background = ((BitmapDrawable) attributes.getDrawable(R.styleable.ClockView_background)).getBitmap();
    }


    private void initPaint(){
        pointerPaint = new Paint();
        pointerPaint.setAntiAlias(true);
        pointerPaint.setStrokeWidth(DEFAULT_STROKE_WIDTH);
        pointerPaint.setDither(true);
        pointerPath = new Path();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int mWidth, mHeight;
        if (widthMode == MeasureSpec.EXACTLY){
            mWidth = widthSize;
        }else {
            mWidth = getPaddingLeft() + radiusDial * 2 + getPaddingRight();
            if (widthMode == MeasureSpec.AT_MOST){
                mWidth = Math.min(mWidth, widthSize);
            }
        }

        if (heightMode == MeasureSpec.EXACTLY){
            mHeight = heightSize;
        }else {
            mHeight = getPaddingTop() + radiusDial * 2 + getPaddingBottom();
            if (heightMode == MeasureSpec.AT_MOST){
                mHeight = Math.min(mHeight, heightSize);
            }
        }
        setMeasuredDimension(mWidth, mHeight);
        mRect = new Rect(0, 0, mWidth, mHeight);
        pointRate = dp2px(mWidth) / 400;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBg(canvas);
        canvas.translate(mRect.right/2,mRect.bottom/2);
        drawPointer(canvas);
    }
    private void drawBg(Canvas canvas){
        canvas.drawBitmap(background,null,mRect,null);
    }

    private void drawPointer(Canvas canvas){
        float currentDegree = min + percent / 100.0f * (max - min) ;
        canvas.rotate(currentDegree);
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
        pointerPath.moveTo(radiusDial - DEFAULT_STROKE_WIDTH * pointRate , 0);
        pointerPath.lineTo(-5 * pointRate, 2 * pointRate);
        pointerPath.lineTo(-5 * pointRate, -2 * pointRate);
        pointerPath.close();
        RectF arcRect = new RectF(-7 * pointRate, -2 * pointRate, -3 * pointRate,2 * pointRate);
        pointerPaint.setStrokeJoin(Paint.Join.ROUND);
        pointerPaint.setStyle(Paint.Style.STROKE);
        pointerPaint.setColor(Color.GRAY);
        canvas.drawArc(arcRect,90,180,false,pointerPaint);
        canvas.drawPath(pointerPath,pointerPaint);
        pointerPaint.setStyle(Paint.Style.FILL);
        pointerPaint.setColor(Color.RED);
        canvas.drawPath(pointerPath,pointerPaint);
        canvas.drawArc(arcRect,90,180,true,pointerPaint);
    }

    public void setPercent(float percent) {
        setAnimator(percent);
    }

    private void setAnimator(final float percent) {
        if (valueAnimator != null && valueAnimator.isRunning()) {
            valueAnimator.cancel();
        }
        animatorDuration = (long) Math.abs(percent - oldPercent) * 20;
        valueAnimator = ValueAnimator.ofFloat(oldPercent, percent).setDuration(animatorDuration);
        valueAnimator.setInterpolator(interpolator);
        valueAnimator.addUpdateListener(animation -> {
            ClockView.this.percent = (float) animation.getAnimatedValue();
            invalidate();
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                oldPercent = percent;
                if (ClockView.this.percent < 0.0) {
                    ClockView.this.percent = 0.0f;
                    invalidate();
                }
                if (ClockView.this.percent > 100.0) {
                    ClockView.this.percent = 100.0f;
                    invalidate();
                }
            }
        });
        valueAnimator.start();

    }

    protected float dp2px(float dpVal) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics());
    }

    protected float sp2px(float spVal) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics());
    }
}
